﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Data;
using System.Reflection;
using AMS.DataAccess.Common;

namespace AMS.DataAccess
{
    /// <summary>
    /// 一个 DataTable 与业务对象互相转换的辅助类。
    /// 采用属性名的列名对应的规则. 业务对象的要操作的属性必不需要有 DBAttribute 子类的标注。
    /// 实际上这个类用于平凡实体类的解析
    /// </summary>
    public class EntityMapper
    {
        /// <summary>
        /// 是否忽略字段名的大小写
        /// </summary>
        public bool IgnoreCase { get; set; }

        #region Public Methods
        /// <summary>
        /// 将DataTable转换成与之对应的业务对象列表。
        /// </summary>
        /// <typeparam name="T">对象类型</typeparam>
        /// <param name="table">数据表</param>
        /// <returns>实体列表</returns>
        public List<T> DataTableToEntityList<T>(DataTable table)
        {
            if (table == null)
            {
                return null;
            }

            var columnNameToIndexMap = MakeObjectToDataTableColumnMap(table, typeof(T));

            List<T> list = new List<T>();
            foreach (DataRow row in table.Rows)
            {
                list.Add(DataRowToEntityInternal<T>(row, columnNameToIndexMap));
            }

            return list;
        }

        /// <summary>
        /// 将一个DataRow转换成一个业务实体对象。
        /// </summary>
        /// <typeparam name="T">对象类型</typeparam>
        /// <param name="row">数据行</param>
        /// <returns>实体列表</returns>
        public T DataRowToEntity<T>(DataRow row)
        {
            var columnNameToIndexMap = MakeObjectToDataTableColumnMap(row.Table, typeof(T));
            return DataRowToEntityInternal<T>(row, columnNameToIndexMap);
        }

        /// <summary>
        /// 将对象的值转到DataRow对应的值中
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="entity"></param>
        /// <param name="row"></param>
        public void EntityToDataRow<T>(T entity, DataRow row)
        {
            Dictionary<string, int> map = MakeObjectToDataTableColumnMap(row.Table, typeof(T));
            EntityToDataRowInternal<T>(entity, row, map);
        }

        /// <summary>
        /// 业务对象列表映射到DataTable
        /// </summary>
        /// <typeparam name="T">对象类型</typeparam>
        /// <param name="entityList">对象列表</param>
        /// <param name="table">数据表</param>
        public void EntityListToRows<T>(List<T> entityList, DataTable table)
        {
            Dictionary<string, int> map = MakeObjectToDataTableColumnMap(table, typeof(T));
            foreach (T bo in entityList)
            {
                DataRow row = table.NewRow();
                EntityToDataRowInternal<T>(bo, row, map);
                table.Rows.Add(row);
            }
        }
        #endregion

        #region Private Methods
        /// <summary>
        /// 实现将一个DataRow转换成一个业务实体对象
        /// </summary>
        /// <typeparam name="T">对象类型</typeparam>
        /// <param name="row">数据行</param>
        /// <param name="columnNameToIndexMap">列映射</param>
        /// <returns>实体对象</returns>
        private T DataRowToEntityInternal<T>(DataRow row, Dictionary<string, int> columnNameToIndexMap)
        {
            var propertyMap = GetObjectProperties(typeof(T));
            T obj = Activator.CreateInstance<T>();
            foreach (KeyValuePair<string, int> item in columnNameToIndexMap)
            {
                PropertyInfo property = propertyMap[item.Key];
                int columnIndex = item.Value;

                SetValue<T>(obj, property, row[columnIndex]);
            }

            return obj;
        }

        /// <summary>
        /// 设置属性值
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="obj"></param>
        /// <param name="property"></param>
        /// <param name="value"></param>
        protected virtual void SetValue<T>(T obj, PropertyInfo property, object value)
        {
            // NOTE: 如果要拦截赋值过程，进行其它处理，可以在调用这个方法之前
            ObjectUtils.SetValue<T>(obj, property, value);
        }

        /// <summary>
        /// 将对象的值转到DataRow对应的值中，使用给定的映射
        /// </summary>
        /// <typeparam name="T">业务对象类型</typeparam>
        /// <param name="entity">业务对象</param>
        /// <param name="row">数据行</param>
        /// <param name="columnNameToIndexMap">列名与列索引的对应关系表</param>
        private void EntityToDataRowInternal<T>(T entity, DataRow row, Dictionary<string, int> columnNameToIndexMap)
        {
            var propertyMap = GetObjectProperties(typeof(T));
            foreach (KeyValuePair<string, int> item in columnNameToIndexMap)
            {
                int columnIndex = columnNameToIndexMap[item.Key];
                PropertyInfo property = propertyMap[item.Key];

                object value = GetValue(entity, property);
                row[columnIndex] = value;
            }
        }
        /// <summary>
        /// 获取字段的值
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="entity"></param>
        /// <param name="property"></param>
        /// <returns></returns>
        protected virtual object GetValue<T>(T entity, PropertyInfo property)
        {
            return property.GetValue(entity, null);
        }

        /// <summary>
        /// 从缓存中获取对象的属性
        /// </summary>
        /// <param name="objectType"></param>
        /// <returns></returns>
        protected virtual Dictionary<string, PropertyInfo> GetObjectProperties(Type objectType)
        {
            if (IgnoreCase)
            {
                ObjectInfo objectInfo = ObjectUtils.GetEntityObjectInfoIgnoreCase(objectType);
                return objectInfo.Properties;
            }
            else
            {
                ObjectInfo objectInfo = ObjectUtils.GetEntityObjectInfo(objectType);
                return objectInfo.Properties;
            }
        }

        /// <summary>
        /// 获取与Table的列名对应的实体属性的一个映射表
        /// </summary>
        /// <param name="table"></param>
        /// <param name="type"></param>
        /// <returns></returns>
        private Dictionary<string, int> MakeObjectToDataTableColumnMap(DataTable table, Type type)
        {
            var map = GetObjectProperties(type);
            Dictionary<string, int> columnNamToIndex = new Dictionary<string, int>();
            for (int i = 0; i < table.Columns.Count; i++)
            {
                string columnName = table.Columns[i].ColumnName;
                if (IgnoreCase)
                {
                    columnName = columnName.ToUpper();
                }
                if (map.ContainsKey(columnName))
                {
                    columnNamToIndex.Add(columnName, i);
                }
            }

            return columnNamToIndex;
        }
        #endregion
    }
}
